package top.as.sean.biz.system.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import top.as.sean.biz.system.IMenu;
import top.as.sean.biz.system.IRole;
import top.as.sean.biz.system.IRoleMenu;
import annotation.Dict;
import top.as.sean.common.constant.Constant;
import top.as.sean.common.exception.GlobalException;
import top.as.sean.common.id.SnowflakeId;
import top.as.sean.common.response.RespStatus;
import top.as.sean.common.tree.TreeUtil;
import top.as.sean.dao.dto.system.RoleAdd;
import top.as.sean.dao.dto.system.RoleAuth;
import top.as.sean.dao.dto.system.RoleEdit;
import top.as.sean.dao.dto.system.RolePage;
import top.as.sean.dao.entity.pojo.system.Role;
import top.as.sean.dao.mapper.system.RoleMapper;
import top.as.sean.dao.vo.system.RoleMenuTree;
import top.as.sean.dao.vo.system.RoleMenuVo;
import top.as.sean.dao.vo.system.RoleVo;

import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 角色
 *
 * @author: ArchieSean
 * @create: 2022/11/8
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RoleImpl extends ServiceImpl<RoleMapper, Role> implements IRole {
    /**
     * 角色-菜单
     */
    private final IRoleMenu iRoleMenu;
    /**
     * 菜单
     */
    private final IMenu iMenu;

    /**
     * 新增角色
     *
     * @param dto 入参
     * @return boolean
     */
    @Override
    public Boolean addRole(RoleAdd dto) {
        //校验角色名称唯一性
        validateRoleName(dto.getRoleName());

        Role insertRole = new Role();
        BeanUtils.copyProperties(dto, insertRole);
        insertRole.setRoleId(SnowflakeId.createId());
        return save(insertRole);
    }

    /**
     * 角色名称校验，可用： true
     *
     * @param roleName 角色名称
     */
    private void validateRoleName(@NotNull String roleName) {
        boolean flag = lambdaQuery().eq(Role::getRoleName, roleName)
                .exists();
        if (flag) {
            throw new GlobalException(RespStatus.ROLE_NAME_EXISTED);
        }
    }

    /**
     * 分页查询
     *
     * @param dto 入参
     * @return page
     */
    @Dict
    @Override
    public Page<RoleVo> getPage(RolePage dto) {
        Page<Role> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        lambdaQuery()
                .like(StringUtils.hasLength(dto.getRoleName()), Role::getRoleName, dto.getRoleName())
                .eq(StringUtils.hasLength(dto.getStatus()), Role::getStatus, dto.getStatus())
                .page(page);
        return (Page<RoleVo>) page.convert(role -> {
            RoleVo roleVo = new RoleVo();
            BeanUtils.copyProperties(role, roleVo);
            roleVo.setStatusDict(role.getStatus());
            return roleVo;
        });
    }

    /**
     * 编辑角色
     *
     * @param dto 入参
     * @return boolean
     */
    @Override
    public Boolean editRole(RoleEdit dto) {
        //查询角色
        Role role = lambdaQuery()
                .eq(Role::getRoleId, dto.getRoleId())
                .one();
        if (ObjectUtils.isEmpty(role)) {
            throw new GlobalException(RespStatus.ROLE_NOT_FOUND);
        }
        //校验是否更改的是角色名称
        if (StringUtils.hasLength(dto.getRoleName()) && !role.getRoleName().equals(dto.getRoleName())) {
            validateRoleName(dto.getRoleName());
        }
        Role editRole = new Role();
        BeanUtils.copyProperties(dto, editRole);
        return updateById(editRole);
    }

    /**
     * 删除角色
     *
     * @param ids 角色id
     * @return boolean
     */
    @Override
    public Boolean deleteRole(List<Long> ids) {
        //避免删除超管
        ids.removeIf(id -> id.equals(BigDecimal.ZERO.longValue()));
        //1. 删除角色-菜单绑定
        //todo 删除角色-菜单绑定
        //2. 删除用户-角色绑定
        //todo 删除用户-角色绑定
        //3. 删除角色
        removeBatchByIds(ids);
        return Boolean.TRUE;
    }

    /**
     * 角色授权
     *
     * @param roleId 角色id
     * @return obj
     */
    @Override
    public RoleMenuVo getMenuTree(Long roleId) {
        //1. 查询中间表、获取所有拥有的菜单id
        List<Long> menuIds = iRoleMenu.queryMenuIdByRoleId(roleId);
        //2.查询树形结构数据
        List<RoleMenuTree> trees = iMenu.queryAllMenu();
        //3.从menuIds中去除有子节点的id
        Set<Long> parentIds = trees.parallelStream().map(RoleMenuTree::getParentId).collect(Collectors.toSet());
        menuIds = menuIds.parallelStream().filter(menuId -> !parentIds.contains(menuId)).collect(Collectors.toList());
        //构建树形
        trees = TreeUtil.build(trees, BigDecimal.ZERO.longValue(),
                Comparator.comparing(RoleMenuTree::getOrderNum));
        return RoleMenuVo.builder()
                .tree(trees)
                .currentNodeKey(menuIds)
                .build();
    }

    /**
     * 授权
     *
     * @param dto 入参
     * @return boolean
     */
    @Override
    public Boolean authorization(RoleAuth dto) {
        //1. 删除授权
        iRoleMenu.deleteByRoleId(dto.getRoleId());
        //2.新增授权
        iRoleMenu.addBatch(dto);
        return Boolean.TRUE;
    }

    /**
     * 查询所有可用角色
     *
     * @return list
     */
    @Override
    public List<Role> getAll() {
        return lambdaQuery()
                .eq(Role::getStatus, Constant.CommonStatus.ENABLE.getCode())
                .ne(Role::getRoleId,BigDecimal.ZERO.longValue())
                .list();
    }
}
